﻿using System;
using System.Collections.Generic;
using System.Linq;
using AutoMapper;
using ERP.BLL.Common;
using ERP.BLL.MenuApp.Dtos;
using ERP.BLL.Model;
using ERP.Common.Extensions;
using ERP.Common.Model;
using ERP.IDAL;
using ERP.IDAL.IRepositories.basic;
using ERP.Model.Entities;

namespace ERP.BLL.MenuApp
{
    public class MenuService : IMenuService
    {
        private readonly IMenuRepository _menuReposity;
        private readonly IUserRepository _userReposity;
        private readonly IRoleRepository _roleReposity;
        private readonly IUserRoleRepository _userRoleReposity;
        private readonly IRoleMenuRepository _roleMenuReposity;
        private readonly IUnitOfWork _unitOfWork;

        public MenuService(
            IMenuRepository menuReposity,
            IUserRepository userReposity,
            IRoleRepository roleReposity,
            IUserRoleRepository userRoleReposity,
            IRoleMenuRepository roleMenuReposity,
            IUnitOfWork unitOfWork)
        {
            _menuReposity = menuReposity;
            _userReposity = userReposity;
            _roleReposity = roleReposity;
            _userRoleReposity = userRoleReposity;
            _roleMenuReposity = roleMenuReposity;
            _unitOfWork = unitOfWork;
        }

        public List<MenuDto> GetActionList()
        {
            var actions = _menuReposity.GetAllListAsync(it => !it.IsMenu).Result.OrderBy(it => it.Sort);
            return Mapper.Map<List<MenuDto>>(actions);
        }

        public PagedResultModel<MenuDto> GetActionPageListByMenu(SearchActionModel search)
        {
            int rowCount;
            if (!search.ParentId.IsEmpty())
            {
                var menus = _menuReposity.GetAllListAsync().Result;
                var menuIds = CollectionExt.GetSons(Mapper.Map<List<SimpleTreeModel>>(menus), search.ParentId).Select(t => t.Id).ToList();

                return Mapper.Map<List<MenuDto>>(_menuReposity.LoadPageList(search.PageIndex, search.PageSize, out rowCount,
                                      r => menuIds.Contains(r.Id), t => t.IsMenu, false)).ApplyToPage(search.PageIndex, search.PageSize, rowCount);

            }
            return Mapper.Map<List<MenuDto>>(_menuReposity.LoadPageList(search.PageIndex, search.PageSize, out rowCount,
                                      r => true, t => t.IsMenu, false)).ApplyToPage(search.PageIndex, search.PageSize, rowCount);
        }
        public bool Update(MenuDto dto)
        {
            return _menuReposity.UpdateAsync(Mapper.Map<Menu>(dto),
                       t =>
                       {
                           t.Name = dto.Name;
                           t.ParentId = dto.ParentId;
                           t.Icon = dto.Icon;
                           t.Url = dto.Url;
                           t.Sort = dto.Sort;
                           t.Enabled = dto.Enabled;
                           t.Comment = dto.Comment;
                           t.IsMenu = dto.IsMenu;
                       }) != null;
        }

        public void Delete(Guid id)
        {
            _menuReposity.Delete(it => it.ParentId == id);
            _menuReposity.Delete(id);
        }

        public int Insert(MenuDto dto)
        {
            const string name = "Name";
            const string url = "Url";
            var checkName = CheckIsExist(name, dto.Name);
            var checkUrl = CheckIsExist(url, dto.Url);
            if (checkName || !(!checkUrl | dto.Url == "#")) return checkName ? 1 : 2;
            try
            {
                _unitOfWork.BeginTransaction();
                _menuReposity.InsertAsync(Mapper.Map<Menu>(dto));
                _unitOfWork.CommitTransaction();
            }
            catch (Exception)
            {
                _unitOfWork.RollbackTransaction();
                throw;
            }

            return 0;
        }

        public bool CheckIsExist(string attr, string attrValue)
        {
            switch (attr)
            {
                case "Name":
                    {
                        var menu = _menuReposity.FirstOrDefaultAsync(t => t.Name == attrValue).Result;
                        return menu != null;
                    }
                case "Url":
                    {
                        var menu = _menuReposity.FirstOrDefaultAsync(t => t.Url == attrValue).Result;
                        return menu != null;
                    }
            }

            return false;
        }

        public List<MenuDto> GetMenusByUser(string loginName)
        {
            var result = new List<MenuDto>();

            //获取菜单列表
            var allMenus = _menuReposity.GetAllListAsync(it => it.IsMenu).Result;

            //获取当前用户
            var user = _userReposity.FirstOrDefaultAsync(it => it.LoginName == loginName).Result;

            if (user == null)
            {
                return result;
            }

            if (user.LoginName == "admin")
            {
                return Mapper.Map<List<MenuDto>>(allMenus);
            }

            //获取当前用户的角色集合
            var userRoleIds = _userRoleReposity.GetAllListAsync(t => t.UserId == user.Id).Result.Select(t => t.RoleId)
                .ToList();

            var menuIds = new List<Guid>();

            foreach (var role in userRoleIds)
            {
                menuIds = menuIds.Union(_roleMenuReposity.GetAllListAsync(t => t.RoleId == role).Result
                    .Select(t => t.MenuId).ToList()).ToList();
                foreach (var item in menuIds)
                {
                    var parentIds = CollectionExt.GetFatherList(Mapper.Map<List<SimpleTreeModel>>(allMenus), item)
                        .Select(t => t.Id).ToList();
                    menuIds = menuIds.Union(parentIds).ToList();
                }
            }

            allMenus = allMenus.Where(it => menuIds.Contains(it.Id)).ToList();
            return Mapper.Map<List<MenuDto>>(allMenus);
        }

        public List<MenuDto> GetActionsByUser(string loginName)
        {
            var result = new List<MenuDto>();

            //获取菜单列表
            var allActions = _menuReposity.GetAllListAsync(it => !it.IsMenu).Result;
            //获取当前用户
            var user = _userReposity.FirstOrDefaultAsync(it => it.LoginName == loginName).Result;

            if (user == null)
            {
                return result;
            }

            if (user.LoginName == "admin")
            {
                return Mapper.Map<List<MenuDto>>(allActions);
            }

            //获取当前用户的角色集合
            var userRoles = _userRoleReposity.GetAllListAsync(t => t.UserId == user.Id).Result.Select(t => t.RoleId)
                .ToList();

            var menuIds = new List<Guid>();

            foreach (var role in userRoles)
            {
                menuIds = menuIds.Union(_roleMenuReposity.GetAllListAsync(t => t.RoleId == role).Result
                    .Select(t => t.MenuId).ToList()).ToList();
            }

            allActions = allActions.Where(it => menuIds.Contains(it.Id)).ToList();
            return Mapper.Map<List<MenuDto>>(allActions);
        }

        public List<UserPermission> GetUsersPermissions()
        {
            var userRoles = _userRoleReposity.GetAllIncluding(t => t.User).ToList();
            var roleActions = _roleMenuReposity.GetAllIncluding(t => t.Menu).ToList();

            var userPermission = from u in userRoles
                                 from m in roleActions
                                 where (u.RoleId == m.RoleId && !m.Menu.IsMenu)
                                 select new UserPermission { Name = u.User.LoginName, Url = m.Menu.Url };
            return userPermission.DistinctBy(t => new { t.Name, t.Url }).ToList();
        }

        public List<RolePermission> GetRolesPermissions()
        {
            return _roleMenuReposity.GetAllIncluding(t => t.Menu).Select(t => new RolePermission { Name = t.RoleId.ToString(), Url = t.Menu.Url }).ToList();
        }

        public List<SimpleTreeDto> GetActionTree()
        {
            var allMenuList = Mapper.Map<List<MenuDto>>(_menuReposity.GetAllListAsync(r => r.Url != "#").Result);

            var rootNodeList = new List<SimpleTreeDto>();

            foreach (var parentNode in allMenuList.Where(t => t.IsMenu))
            {
                var rootNode = new SimpleTreeDto
                {
                    Id = parentNode.Id,
                    ParentId = parentNode.ParentId,
                    Name = parentNode.Name,
                };
                rootNode.Children = CreateActionChildren(allMenuList, rootNode);
                rootNodeList.Add(rootNode);
            }

            return rootNodeList;
        }

        public List<SimpleTreeDto> CreateActionChildren(List<MenuDto> allActionList, SimpleTreeDto dto)
        {
            return allActionList.Where(t => t.ParentId == dto.Id && !t.IsMenu).Select(child =>
                new SimpleTreeDto
                {
                    Id = child.Id,
                    ParentId = child.ParentId,
                    Name = child.Name,
                }).ToList();
        }

        public bool BatchInsertAction(List<ActionDto> dtos)
        {
            try
            {
                _unitOfWork.BeginTransaction();

                foreach (var item in dtos)
                {
                    var parentMenu = _menuReposity
                        .FirstOrDefaultAsync(s => s.Url == item.ControlName && s.IsMenu).Result;
                    if (parentMenu == null) continue;
                    {
                        var menuDto = new MenuDto()
                        {
                            ParentId = parentMenu.Id,
                            Name = item.FunctionName,
                            Url = "/Api/" + item.ControlName + "/" + item.MethodName,
                            Comment = "无",
                        };

                        var menuResult = _menuReposity.FirstOrDefaultAsync(s => s.Url == menuDto.Url).Result;

                        if (menuResult != null)
                        {
                            menuResult.Name = item.FunctionName;
                            menuResult.IsMenu = false;
                            _menuReposity.UpdateAsync(menuResult);
                        }
                        else
                        {
                            _menuReposity.InsertAsync(Mapper.Map<Menu>(menuDto));
                        }
                    }
                }

                _unitOfWork.CommitTransaction();
            }
            catch (Exception)
            {
                _unitOfWork.RollbackTransaction();
                throw;
            }

            return true;
        }
    }
}